package cn.ronghuanet.base.service.impl;

import cn.ronghuanet.base.constants.BaseConstans;
import cn.ronghuanet.base.dto.RegisterSendSmsDTO;
import cn.ronghuanet.base.exception.BusinessException;
import cn.ronghuanet.base.exception.ResponseCode;
import cn.ronghuanet.base.exception.RonghuaAssert;
import cn.ronghuanet.base.service.IVerifyCodeService;
import cn.ronghuanet.base.util.SmsUtil;
import cn.ronghuanet.base.util.StrUtils;
import cn.ronghuanet.base.util.VerifyCodeUtils;
import cn.ronghuanet.user.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Service
public class VerifyCodeServiceImpl implements IVerifyCodeService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserMapper userMapper;

    @Override
    public String getImageCodeByRegister(String imageCodeKey) throws IOException {
        // 1 创建4位数的图片验证码
        String randomStr = StrUtils.getComplexRandomString(4);
        System.out.println(randomStr);

        // 2 将图片验证码存入redis
        // 因为前端不止注册会获取图片验证码,其他场景也有可能会获取,那每个场景它们都要传递uuid过来,为了防止多个场景的uuid万一重复
        // 所以在这块存入redis的时候,key的前面加一个场景前缀
        // 图片验证码超过5分钟如果没有填写就应该过期
        redisTemplate.opsForValue().set("register_imagecode_"+imageCodeKey,randomStr,5, TimeUnit.MINUTES);

        // 3 根据验证码生成图片的base64加密字符串
        String base64Str = VerifyCodeUtils.VerifyCode(120, 42, randomStr);

        // 4 将base64加密字符串返回
        return base64Str;
    }

    @Override
    public void registerSendSms(RegisterSendSmsDTO dto) {
        // 1 参数校验
        validateParam(dto);

        String redisKey = BaseConstans.REGISTER_SMS_SEND_KEY + dto.getMobile();
        sendSmsCode(redisKey);
    }

    private void sendSmsCode(String redisKey) {
        // 2 根据手机号从redis中获取value
        Object smsCodeByRedis = redisTemplate.opsForValue().get(redisKey);
        String smsCode = "";
        if(Objects.nonNull(smsCodeByRedis)){
            // 2.1 如果存在
            // 2.1.1 要获取这个验证码和发送时间   smsCodeByRedis ->  验证码:发送时间
            String smsCodeStr = smsCodeByRedis.toString();
            smsCode = smsCodeStr.split(":")[0];
            String sendTime = smsCodeStr.split(":")[1];
            // 2.1.2 判断是否已过重发时间,如果没过返回错误信息 当前时间-发送时间 > 1分钟就是过了 <1分钟没过
            if(System.currentTimeMillis() - Long.parseLong(sendTime) < 60*1000){
                throw new BusinessException(ResponseCode.RESPONSE_CODE_40019);
            }
        }else{
            // 2.2 如果不存在
            // 2.2.1 生成随机数
            smsCode = StrUtils.getRandomString(6);
        }
        // 3 将验证码和发送时间存入redis
        String value = smsCode+":"+System.currentTimeMillis();
        redisTemplate.opsForValue().set(redisKey,value,5,TimeUnit.MINUTES);

        // 4 发送短信给用户
        String msg = "注册验证码为%s,请不要随意泄露!";
        msg = String.format(msg,smsCode);
        //SmsUtil.sendSms(dto.getMobile(),msg);
        System.out.println(msg);
    }

    @Override
    public void bindSendSms(Map<String, String> map) {
        // 1 校验手机号是否为空
        RonghuaAssert.isNotBlank(map.get("phone"),ResponseCode.RESPONSE_CODE_40025);

        // 2 发送手机验证码
        String redisKey = BaseConstans.BIND_SMS_SEND_KEY + map.get("phone");
        sendSmsCode(redisKey);
    }

    private void validateParam(RegisterSendSmsDTO dto) {
        // 1.1 参数非空校验  jsr303校验
        // 1.2 uuid非空判断,如果为空,返回500错误
        RonghuaAssert.isNotBlank(dto.getImageCodeKey(), ResponseCode.RESPONSE_CODE_500);
        // 1.3 校验图片验证码是否正确 如果错误,返回错误信息
        // 1.3.1 先根据key uuid获取redis中的图片验证码
        Object imageCodeByRedis = redisTemplate.opsForValue().get("register_imagecode_" + dto.getImageCodeKey());
        // 1.3.2 如果redis中获取的图片验证码为空,说明已经过期,要返回错误信息
        RonghuaAssert.isNotNull(imageCodeByRedis,ResponseCode.RESPONSE_CODE_40016);
        // 1.3.3 和前端传递的验证码做校验  校验图片验证码要忽略大小写
        if(!dto.getImageCode().equalsIgnoreCase(imageCodeByRedis.toString())){
            throw new BusinessException(ResponseCode.RESPONSE_CODE_40017);
        }

        // 1.4 校验手机号是否已注册,如果已注册返回错误信息
        Integer count = userMapper.loadCountByPhone(dto.getMobile());
        if(count > 0){
            throw new BusinessException(ResponseCode.RESPONSE_CODE_40018);
        }
    }
}
